package client;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import common.VSM_AboutScreen;
import common.VSM_NetWrapper;
import common.VSM_ProjectInfo;
import common.VSM_WrapperAcknowledgment;
import common.VSM_WrapperConnect;
import common.VSM_WrapperData;
import common.VSM_WrapperDisconnect;

public class VSM_Sensor extends JFrame implements Runnable, ActionListener
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 7958802783369116325L;
	private final JMenuBar menuBar = new JMenuBar();
	private final JMenu menuFile = new JMenu("File");
	private final JMenu menuHelp = new JMenu("Help");
	private final JMenuItem menuItemOn = new JMenuItem("Turn Sensor On");
	private final JMenuItem menuItemOff = new JMenuItem("Turn Sensor Off");
	private final JMenuItem menuItemCrlscr = new JMenuItem("Clear Log");
	private final JMenuItem menuItemConfig = new JMenuItem("Configurations");
	private final JMenuItem menuItemExit = new JMenuItem("Exit");
	private final JMenuItem menuItemHelp = new JMenuItem("Help contents");
	private final JMenuItem menuItemAbout = new JMenuItem("About");

	private final JTextArea logText = new JTextArea();

	private final JScrollPane backPanel = new JScrollPane(logText, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

	private String serverID;
	private int portNumber;
	private String sensorId;
	private String sensorUnit;
	private double sensorMinValue = 0.0;
	private double sensorMaxValue;
	private int timeInterval = 3000;
	private boolean isRunning = false;
	private Socket myClientSocket;
	private ObjectOutputStream toServerStream = null;
	private ObjectInputStream fromServerStream = null;

	VSM_Sensor()
	{
		Random num = new Random();
		
		serverID = VSM_ProjectInfo.getServerid();
		portNumber = VSM_ProjectInfo.getPortnumber();
		sensorId = Integer.toString(num.nextInt(Integer.MAX_VALUE));
		sensorUnit = "V";
		sensorMaxValue = num.nextInt(9999)+1;
		
		this.createScreen();
	}

	private void createScreen()
	{
		this.setTitle(VSM_ProjectInfo.getProjectname() + " - Sensor (" + VSM_ProjectInfo.getProjectversion() + ")");
		this.setSize(800, 200);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setLocationRelativeTo(null);
		this.setLayout(new BorderLayout(5, 5));
	
		this.setJMenuBar(menuBar);
		
		this.add(backPanel,BorderLayout.CENTER);

		menuBar.add(menuFile);
		menuBar.add(menuHelp);

		menuFile.add(menuItemOn);
		menuFile.add(menuItemOff);

		menuFile.addSeparator();

		menuFile.add(menuItemConfig);

		menuFile.addSeparator();

		menuFile.add(menuItemCrlscr);

		menuFile.addSeparator();

		menuFile.add(menuItemExit);

		menuHelp.add(menuItemHelp);
		menuHelp.add(menuItemAbout);
		
		logText.append("This is the sensor: " + sensorId + ", ranging from " + sensorMinValue + " to " + sensorMaxValue + " " + sensorUnit);
		
		//this.setSize(getPreferredSize().width, getPreferredSize().height + 25);
		
		menuItemOn.addActionListener(this);
		menuItemOff.addActionListener(this);
		
		menuItemConfig.addActionListener(this);
		
		menuItemCrlscr.addActionListener(this);
		
		menuItemExit.addActionListener(this);
		
		
		menuItemAbout.addActionListener(this);

		menuItemOff.setEnabled(false);
		
		
	}

	@Override
	public void actionPerformed(ActionEvent arg0)
	{
		if(arg0.getSource() == menuItemOn)
		{
			startSensor();
		}
		
		else if(arg0.getSource() == menuItemOff)
		{
			stopSensor();
		}
		
		else if(arg0.getSource() == menuItemConfig)
		{
			configureSensor();
		}
		
		else if(arg0.getSource() == menuItemCrlscr)
		{
			clearLog();
		}
		
		else if(arg0.getSource() == menuItemExit)
		{
			this.dispose();
		}
		
		else if(arg0.getSource() == menuItemAbout)
		{
			VSM_AboutScreen aboutWindow = new VSM_AboutScreen(this,VSM_ProjectInfo.getProjectname() + " (" + VSM_ProjectInfo.getProjectversion() + ") - About");
			aboutWindow.setModal(true);
			aboutWindow.setVisible(true);
		}

	}

	private void configureSensor()
	{
		if(isRunning == true)
		{
			stopSensor();
			VSM_ConfigScreen configWindow = new VSM_ConfigScreen(this,VSM_ProjectInfo.getProjectname() + " - Sensor (" + VSM_ProjectInfo.getProjectversion() + ") - Configuration");
			configWindow.setModal(true);
			configWindow.setVisible(true);
			restartSensor();
		}
		else
		{
			VSM_ConfigScreen configWindow = new VSM_ConfigScreen(this,VSM_ProjectInfo.getProjectname() + " - Sensor (" + VSM_ProjectInfo.getProjectversion() + ") - Configuration");
			configWindow.setModal(true);
			configWindow.setVisible(true);
		}
		
	}

	void restartSensor()
	{
		if(isRunning == false)
		{
			startSensor();
		}
	}

	private void clearLog()
	{
		logText.setText("");
		logText.append("This is the sensor: " + sensorId + ", ranging from " + sensorMinValue + " to " + sensorMaxValue + " " + sensorUnit);
	}

	private void startSensor()
	{
		menuItemOn.setEnabled(false);
		menuItemOff.setEnabled(true);
		
		Thread thread = new Thread(this);
		isRunning = true;
		thread.start();
	}

	private void stopSensor()
	{
		if(isRunning == true)
		{
			if(myClientSocket != null)
			{
				VSM_WrapperDisconnect disconnect = new VSM_WrapperDisconnect(myClientSocket.getLocalAddress().toString(), myClientSocket.getLocalPort(), sensorId);
				try
				{
					VSM_NetWrapper wrapper;
					toServerStream.writeObject(disconnect);
					logText.append("\nClosing connection to the server " + serverID + ", port " + portNumber + ".");
					
					wrapper = (VSM_NetWrapper)fromServerStream.readObject();
					
					if(wrapper instanceof VSM_WrapperAcknowledgment)
					{
						logText.append("\nConnection finished to the server at " + serverID + ", port " + portNumber + ". Requested by sensor.");
					}
				} catch (IOException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ClassNotFoundException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			menuItemOn.setEnabled(true);
			menuItemOff.setEnabled(false);
			
			isRunning = false;
		}
	}

	@Override
	public void run()
	{		
		Random num = new Random();
		double currentValue = 0.0;
		
		try
		{
			myClientSocket = new Socket(serverID, portNumber);
			
			toServerStream = new ObjectOutputStream(myClientSocket.getOutputStream());
			fromServerStream = new ObjectInputStream(myClientSocket.getInputStream());
			currentValue = (num.nextDouble() * sensorMaxValue);
			VSM_NetWrapper wrapper;
			
			//System.out.println("LocalSocketAddress: " + myClientSocket.getLocalSocketAddress().toString() + "\nInetAddess: " + myClientSocket.getInetAddress() + "\nLocalAddress: " + myClientSocket.getLocalAddress() + "\n" + myClientSocket.getLocalPort());
			VSM_WrapperConnect connect = new VSM_WrapperConnect(myClientSocket.getLocalAddress().toString(), myClientSocket.getLocalPort(), sensorId, sensorUnit, sensorMinValue, sensorMaxValue, timeInterval, currentValue);
			toServerStream.writeObject(connect);
			
			wrapper = (VSM_NetWrapper)fromServerStream.readObject();
			
			if(wrapper instanceof VSM_WrapperAcknowledgment)
			{
				logText.append("\nConnection stablished to the server at " + serverID + ", port " + portNumber);
			}
			
			while(isRunning == true)
			{
				currentValue = (num.nextDouble() * sensorMaxValue);
				VSM_WrapperData data = new VSM_WrapperData(currentValue);
				toServerStream.writeObject(data);
				
				wrapper = (VSM_NetWrapper)fromServerStream.readObject();
				
				if(wrapper instanceof VSM_WrapperAcknowledgment)
				{
					logText.append("\nData reception of " + currentValue + " confirmed by the server " + serverID + ", port " + portNumber + ".");
				}
				
				else if(wrapper instanceof VSM_WrapperDisconnect)
				{
					logText.append("\nConnection to the server " + serverID + " closed by the network.");
					myClientSocket.close();
					break;
				}
				
				Thread.currentThread();
				Thread.sleep(timeInterval);
			}
			
		} catch (UnknownHostException e2)
		{
			// TODO Auto-generated catch block
			e2.printStackTrace();
			stopSensor();
		} catch (IOException e2)
		{
			// TODO Auto-generated catch block
			logText.append("\nError while communicating with server at " + serverID + ". Connection refused.");
			JOptionPane.showMessageDialog(this, "Error while communicating with server at " + serverID + ". Connection refused.", "Network error", JOptionPane.ERROR_MESSAGE);
			stopSensor();
			//e2.printStackTrace();
		} catch (ClassNotFoundException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			stopSensor();
		} catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			stopSensor();
		}
		finally
		{
			try
			{
				if(myClientSocket != null)
					myClientSocket.close();
			} catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
				stopSensor();
			}
		}
		
		stopSensor();
	}

	String getServerID()
	{
		return serverID;
	}

	void setServerID(String serverID)
	{
		if(this.serverID.compareTo(serverID) != 0)
		{
			this.serverID = serverID;
			logText.append("\nServer Name or IP adress changed to: " + serverID);
		}
	}

	int getPortNumber()
	{
		return portNumber;
	}

	void setPortNumber(int portNumber)
	{
		if(this.portNumber != portNumber)
		{
			this.portNumber = portNumber;
			logText.append("\nTCP port at server changed to: " + portNumber);
		}
	}

	String getSensorId()
	{
		return sensorId;
	}

	void setSensorId(String sensorId)
	{
		if(this.sensorId.compareTo(sensorId) != 0)
		{
			this.sensorId = sensorId;
			logText.append("\nSensor name changed to: " + sensorId);
		}
	}

	String getSensorUnit()
	{
		return sensorUnit;
	}

	void setSensorUnit(String sensorUnit)
	{
		if(this.sensorUnit.compareTo(sensorUnit) != 0)
		{
			this.sensorUnit = sensorUnit;
			logText.append("\nSensor name changed to: " + sensorUnit);
		}
	}

	double getSensorMinValue()
	{
		return sensorMinValue;
	}

	void setSensorMinValue(double sensorMinValue)
	{
		if(this.sensorMinValue != sensorMinValue)
		{
			this.sensorMinValue = sensorMinValue;
			logText.append("\nSensor minimum value changed to: " + sensorMinValue);
		}
	}

	double getSensorMaxValue()
	{
		return sensorMaxValue;
	}

	void setSensorMaxValue(double sensorMaxValue)
	{
		if(this.sensorMaxValue != sensorMaxValue)
		{
			this.sensorMaxValue = sensorMaxValue;
			logText.append("\nSensor maximum value changed to: " + sensorMaxValue);
		}
	}

	int getTimeInterval()
	{
		return timeInterval;
	}

	void setTimeInterval(int timeInterval)
	{
		if(this.timeInterval != timeInterval)
		{
			this.timeInterval = timeInterval;
			logText.append("\nSensor time interval changed to: " + timeInterval);
		}
	}
}
